//    Copyright (C) Kherty.  All rights reserved.
#region

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.IO.Packaging;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

#endregion

namespace OpenLS.Core.Serialization
{
    public class WriteContext : IDisposable
    {
        private static readonly EnumConverter _enumConverter = new EnumConverter();
        private readonly PackagePart _part;
        private readonly XmlWriter _writer;
        private string _namespaceUri;
        private string _prefix;
        private bool _qualifiedAttributes;
        private Dictionary<object, object> _values;

        internal WriteContext(string prefix, string namespaceUri, XmlWriter writer, PackagePart part)
        {
            Contract.Requires((prefix == null) || (namespaceUri != null));
            _writer = writer;
            _part = part;
            _prefix = prefix;
            _namespaceUri = namespaceUri;
        }

        public WriteContext(XmlNamespace n, XmlWriter writer, PackagePart part) :
            this(n.Prefix, n.NamespaceUri, writer, part)
        {
        }

        public WriteContext(string name, string prefix, string namespaceUri, XmlWriter writer, PackagePart part)
        {
            Contract.Requires((prefix == null) || (namespaceUri != null));
            
            if (name == null)
                throw new ArgumentNullException("name");
            _writer = writer;
            _part = part;
            _prefix = prefix;
            _namespaceUri = namespaceUri;
            writer.WriteStartElement(Prefix, name, NamespaceUri);
        }

        public WriteContext(XmlNamespace xmlNamespace, XmlWriter writer, PackagePart part, bool b)
            : this(xmlNamespace, writer, part)
        {
            _qualifiedAttributes = b;
        }

        public bool ForceNamespace
        {
            get { return _qualifiedAttributes; }
        }

        public PackagePart Part
        {
            get { return _part; }
        }
        ///<summary>
        /// create an external relationship 
        ///</summary>
        ///<param name="target">target URI</param>
        ///<param name="relationshipType">type of relationship</param>
        ///<returns></returns>
        public PackageRelationship CreateExternalRelationship(string target, string relationshipType)
        {
            return Part.CreateRelationship(new Uri(cleanupUri(target), UriKind.RelativeOrAbsolute), TargetMode.External,
                                           relationshipType);
        }

        private static string cleanupUri(string s)
        {
            Debug.WriteLine("Writing uri = " + s);
            var result =  s.Replace("\"", "%22").Replace(" ", "%20").Replace("{", "%7b").Replace("}", "%7d").Replace("\r", "%0d")
                .Replace("\n", "%0a").Replace("[", "%5b").Replace("]", "%5d").Replace("^", "%5e").Replace("<", "%3c").Replace(">", "%3e");
            Debug.WriteLine("result = " + result);
            return result;
        }

        public XmlWriter Writer
        {
            get { return _writer; }
        }

        public string Prefix
        {
            get
            {
                Contract.Ensures((_prefix == null) || (_namespaceUri != null));
                return _prefix;
            }
        }

        public string NamespaceUri
        {
            get
            {
                Contract.Ensures((_prefix == null) || (_namespaceUri != null));
                return _namespaceUri;
            }
        }

        #region IDisposable Members

        public virtual void Dispose()
        {
            _writer.WriteEndElement();
        }

        #endregion

        public RelatedWriteContext CreateRelatedContext(XmlNamespace ns, Uri uri, string relationshipType,
                                                        string contentType)
        {
            return CreateRelatedContext(ns.Prefix, ns.NamespaceUri, uri, relationshipType, contentType);
        }

        public RelatedWriteContext CreateRelatedContext(string prefix, string namespaceUri, Uri uri,
                                                        string relationshipType, string contentType, Encoding encoding)
        {
            PackageRelationship relationship;
            PackagePart newPart;
            CreatePart(uri, relationshipType, contentType, out relationship, out newPart);
            Stream stream = newPart.GetStream();
            #if SILVERLIGHT
            var newWriter =  XmlWriter.Create(stream);
            #else
            var newWriter =  new XmlTextWriter(stream, encoding);
            #endif
            #if INDENTED
            newWriter.Indentation = 1;
            newWriter.IndentChar = '\t';
            newWriter.Formatting = System.Xml.Formatting.Indented;
            #endif
            var result = new RelatedWriteContext(prefix, namespaceUri, newWriter, newPart, stream, relationship);
            result.CopyDataFrom(this);
            return result;
            
        }

        public void CreatePart(Uri uri, string relationshipType, string contentType,
                               out PackageRelationship relationship, out PackagePart newPart)
        {
            relationship = _part.CreateRelationship(uri, TargetMode.Internal, relationshipType);
            Uri ucp = PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri);
            newPart = _part.Package.CreatePart(ucp, contentType, CompressionOption.Maximum);
        }

        public RelatedWriteContext CreateRelatedContext(string prefix, string namespaceUri, Uri uri,
                                                        string relationshipType, string contentType)
        {
            RelatedWriteContext result = CreateRelatedContext(prefix, namespaceUri, uri, relationshipType, contentType,
                                                              Encoding.UTF8);
            result._qualifiedAttributes = _qualifiedAttributes;
            return result;
        }

        public object GetData(object key)
        {
            if (_values == null)
                return null;
            if (_values.ContainsKey(key))
                return _values[key];
            return null;
        }

        public void SetData(object key, object value)
        {
            if (_values == null)
                _values = new Dictionary<object, object>();
            if (_values.ContainsKey(key))
                _values.Remove(key);
            _values.Add(key, value);
        }

        /*public WriteContext(string name, XmlWriter writer, PackagePart _part)
        {
            this.writer = writer;
            this._part = _part;
            writer.WriteStartElement(name);
        }*/

        private void SetAttribute(string attributeName, string value)
        {
            if (_qualifiedAttributes)
                _writer.WriteStartAttribute(Prefix, attributeName, NamespaceUri);
            else
                _writer.WriteStartAttribute(attributeName);
            _writer.WriteString(value);
            _writer.WriteEndAttribute();
        }

        public void DeclareNamespace(string prefix, string namespaceUri)
        {
            Writer.WriteStartAttribute(null, prefix, "http://www.w3.org/2000/xmlns/");
            Writer.WriteString(namespaceUri);
            Writer.WriteEndAttribute();
        }

        public void SetEnum<T>(XmlQualifiedName qualifiedName, T value)
        {
            SetEnumWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace, value);
        }

        public void SetEnum<T>(string name, T value)
        {
            SetAttribute(name, _enumConverter.GetValue(value));
        }

        private void SetEnumWithNamespace<T>(string name, XmlNamespace xmlNamespace, T value)
        {
            _writer.WriteStartAttribute(xmlNamespace.Prefix, name, xmlNamespace.NamespaceUri);
            _writer.WriteValue(_enumConverter.GetValue(value));
            _writer.WriteEndAttribute();
        }

        public void SetString(XmlQualifiedName qualifiedName, string value)
        {
            SetStringWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace, value);
        }

        public void SetString(string attributeName, string value)
        {
            Contract.Requires(value != null);
            Contract.Requires(attributeName != null);
            SetAttribute(attributeName, value);
        }

        public WriteContext Write(XmlQualifiedName qualifiedName)
        {
            return Write(qualifiedName.LocalName, qualifiedName.Namespace)
                ;
        }

        public WriteContext Write(string name)
        {
#if DEBUG
            CheckInvariant();
#endif
            WriteContext result = new WriteContext(name, Prefix, NamespaceUri, _writer, _part)
                                      {
                                          _qualifiedAttributes = _qualifiedAttributes,
                                          _values = _values
                                      };
            return result;
        }

#if DEBUG
        [ContractInvariantMethod]
        private void Invariant()
        {
            Contract.Invariant((Prefix == null) || (NamespaceUri != null));
        }
        public void CheckInvariant()
        {
            Contract.Assert((Prefix == null) || (NamespaceUri != null));
        }
#endif

        private WriteContext Write(string name, string prefix, string namespaceUri)
        {
            var result = new WriteContext(name, prefix, namespaceUri, _writer, _part)
                             {
                                 _qualifiedAttributes = _qualifiedAttributes,
                                 _values = _values
                             };
            return result;
        }

        private WriteContext Write(string name, XmlNamespace u)
        {
            var result = new WriteContext(name, u.Prefix, u.NamespaceUri, _writer, _part);
            result._qualifiedAttributes = u.QualifiedAttributes;
            result._values = _values;

            return result;
        }

        private WriteContext WriteAndSwitch(string name, string prefix, string namespaceURI, bool forceNamespaceUri)
        {
            var result = new WriteContext(name, Prefix, NamespaceUri, _writer, _part);
            result._prefix = prefix;
            result._namespaceUri = namespaceURI;
            result._qualifiedAttributes = forceNamespaceUri;
            result._values = _values;
            return result;
        }

        public WriteContext WriteAndSwitch(string name, XmlNamespace n)
        {
            return WriteAndSwitch(name, n.Prefix, n.NamespaceUri, n.QualifiedAttributes);
        }

        public WriteContext Switch(XmlNamespace n)
        {
            var result = new WriteContext(Prefix, NamespaceUri, _writer, _part);
            result._prefix = n.Prefix;
            result._namespaceUri = n.NamespaceUri;
            result._qualifiedAttributes = n.QualifiedAttributes;
            result._values = _values;
            return result;
        }


        public void WriteOptionalString(string elementName, string value)
        {
            if (value == null)
                return;
            using (var c = new WriteContext(elementName, Prefix, NamespaceUri, _writer, _part))
            {
                c._writer.WriteString(value);
            }
        }

        public void WriteElementString(string elementName, string value)
        {
            using (Write(elementName))
            {
                _writer.WriteString(value);
            }
        }

        public void SetOptionalBoolean(string attributeName, bool value, bool defaultValue)
        {
            if (value == defaultValue)
                return;
            SetString(attributeName, value ? "1" : "0");
        }

        public void SetBoolean(string attributeName, bool value)
        {
            SetString(attributeName, value ? "1" : "0");
        }

        public void SetOptionalString(XmlQualifiedName qualifiedName, string value)
        {
            SetOptionalStringWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace, value);
        }

        public void SetOptionalString(string name, string value)
        {
            if (value == null)
                return;
            SetString(name, value);
        }

        public void SetOptionalGuid(string name, Guid? value)
        {
            if (value == null)
                return;
            SetString(name, value.Value.ToString());
        }

        private static string doubleToString(double d)
        {
            if (double.IsPositiveInfinity(d))
                return "INF";
            return d.ToString(CultureInfo.InvariantCulture);
        }

        private static string floatToString(float d)
        {
            if (float.IsPositiveInfinity(d))
                return "INF";
            return d.ToString(CultureInfo.InvariantCulture);
        }


        public void SetOptionalEnum<T>(string name, T value, T defaultValue) where T : struct
        {
            if (value.Equals(defaultValue))
                return;
            SetEnum(name, value);
        }

        public void SetOptionalInteger(string attributeName, int? value)
        {
            if (value == null)
                return;
            SetInteger(attributeName, value.Value);
        }

        public void WriteString(string value)
        {
            writeNonPreservedString(value);
        }

        private void writeNonPreservedString(string value)
        {
            _writer.WriteString(value);
        }

        private void writePreservedStringImp(string value)
        {
            _writer.WriteStartAttribute("xml", "space", null);
            _writer.WriteString("preserve");
            _writer.WriteEndAttribute();
            writeNonPreservedString(value);
        }

        public void WritePreservedString(string value)
        {
            if (value.Length > 0)
            {
                char c1 = value[0];
                char c2 = value[value.Length - 1];
                if (char.IsWhiteSpace(c1) || char.IsWhiteSpace(c2))
                {
                    writePreservedStringImp(value);
                    return;
                }
                WriteString(value);
            }
        }

        public void SetInteger(string attributeName, int value)
        {
            SetAttribute(attributeName, value.ToString());
        }

        public void SetInteger64(string attributeName, long value)
        {
            SetAttribute(attributeName, value.ToString());
        }

        private void SetUInteger(string attributeName, uint value)
        {
            SetAttribute(attributeName, value.ToString());
        }

        public void SetDouble(string attributeName, double value)
        {
            SetAttribute(attributeName, doubleToString(value));
        }

        public void SetFloat(string attributeName, float value)
        {
            SetAttribute(attributeName, floatToString(value));
        }

        public void SetOptionalDouble(string attributeName, double value, double defaultValue)
        {
            if (value == defaultValue)
                return;
            SetString(attributeName, doubleToString(value));
        }


        public void SetOptionalInteger(string name, int value, int defaultValue)
        {
            if (value == defaultValue)
                return;
            SetInteger(name, value);
        }

        public void WriteOptionalElement(IOfficeXmlSerializable element)
        {
#if DEBUG
            CheckInvariant();
#endif
            if (element == null)
                return;
            element.WriteXml(this);
        }


        public void SetOptionalString(string name, string value, string defaultValue)
        {
            Contract.Requires(defaultValue != null);
            Contract.Requires(defaultValue.IndexOf("urn:schemas") == -1);
            if (value != defaultValue)
                SetString(name, value);
        }

        public void WriteOptionalElementAs(IOfficeXmlSerializableAs element, string name)
        {
            if (element == null)
                return;
            element.WriteXmlAs(name, this);
        }

        public void WriteElementAs(IOfficeXmlSerializableAs element, string name)
        {
            Contract.Requires(element != null);
            Contract.Requires(name != null);
            element.WriteXmlAs(name, this);
        }


        public void WarnIf(params string[] name)
        {
            //\\throw new NotImplementedException();
        }


        public void SetOptionalEnum<T>(XmlQualifiedName qualifiedName, T? value) where T : struct
        {
            SetOptionalEnumWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace, value);
        }


        public void SetOptionalEnum<T>(string name, T? value) where T : struct
        {
            if (value == null)
                return;
            SetEnum(name, value.Value);
        }

        public void SetOptionalDouble(string name, double? value)
        {
            if (value == null)
                return;
            SetDouble(name, value.Value);
        }

        public void WriteElement(IOfficeXmlSerializable element)
        {
            Contract.Requires(element != null);
            element.WriteXml(this);
        }

        [CLSCompliant(false)]
        public void WriteOptionalUInteger(string name, uint value, int defaultValue)
        {
            if (value == defaultValue)
                return;
            using (WriteContext c = Write(name))
            {
                c._writer.WriteString(value.ToString());
            }
        }

        public void WriteOptionalElementBooleanAttribute(string name, bool value, bool defaultValue)
        {
            if (value == defaultValue)
                return;
            using (WriteContext c = Write(name))
            {
                c.SetBoolean("val", value);
            }
        }


        public void WriteElementIntegerAttribute(string elementName, string attributeName, int value)
        {
            using (WriteContext c = Write(elementName))
            {
                c.SetInteger(attributeName, value);
            }
        }

        public void WriteElementStringAttribute(string elementName, string attributeName, string value)
        {
            Contract.Requires(value != null);
            using (WriteContext c = Write(elementName))
            {
                c.SetString(attributeName, value);
            }
        }

        public void WriteElementDoubleAttribute(string elementName, string attributeName, double value)
        {
            using (WriteContext c = Write(elementName))
            {
                c.SetDouble(attributeName, value);
            }
        }

        public void WriteOptionalElementIntegerAttribute(string elementName, string attributeName, int value,
                                                         int defaultValue)
        {
            if (value == defaultValue)
                return;
            WriteElementIntegerAttribute(elementName, attributeName, value);
        }

        public void WriteOptionalElementIntegerAttribute(string elementName, int value, int defaultValue)
        {
            WriteOptionalElementIntegerAttribute(elementName, "val", value, defaultValue);
        }

        [CLSCompliant(false)]
        public void WriteElementUIntegerAttribute(string elementName, string attributeName, uint value)
        {
            using (WriteContext c = Write(elementName))
            {
                c.SetUInteger(attributeName, value);
            }
        }

        public void WriteOptionalElementBooleanAttribute(string elementName, bool? value)
        {
            if (value != null)
                using (WriteContext c = Write(elementName))
                    c.SetBoolean("val", value.Value);
        }

        [CLSCompliant(false)]
        public void WriteOptionalElementUIntegerAttribute(string elementName, string attributeName, uint value,
                                                          uint defaultValue)
        {
            if (value == defaultValue)
                return;
            WriteElementUIntegerAttribute(elementName, attributeName, value);
        }

        public void WriteOptionalElementEnumAttribute<T>(string elementName, string attributeName, T value, T default1,
                                                         T default2) where T : struct
        {
            if (value.Equals(default1))
                return;
            using (WriteContext c = Write(elementName))
                c.SetOptionalEnum(attributeName, value, default2);
        }


        [CLSCompliant(false)]
        public void SetOptionalUInteger(string name, uint value, uint defaultValue)
        {
            if (value == defaultValue)
                return;
            SetUInteger(name, value);
        }

        [CLSCompliant(false)]
        public void SetOptionalUInteger(string name, uint? value)
        {
            if (value == null)
                return;
            SetUInteger(name, value.Value);
        }


        public void WriteElementEnumAttribute<T>(string elementName, T value)where T: struct
        {
            WriteElementEnumAttribute(elementName, "val", value);
        }

        public void WriteElementEnumAttribute<T>(string elementName, string attributeName, T value)where T: struct
        {
            using (WriteContext c = Write(elementName))
            {
                c.SetEnum(attributeName, value);
            }
        }

        public void WriteOptionalElementEnumAttribute<T>(string name, T? value) where T : struct
        {
            if (value == null)
                return;
            WriteElementEnumAttribute(name, value.Value);
        }


        public void WriteOptionalElementDoubleAttribute(string elementName, string attributeName, double value,
                                                        double defaultValue)
        {
            if (value == defaultValue)
                return;
            using (WriteContext c = Write(elementName))
            {
                c.SetDouble(attributeName, value);
            }
        }


        private void SetString(string localName, string prefix, string namespaceUri, string value)
        {
            _writer.WriteStartAttribute(prefix, localName, namespaceUri);
            _writer.WriteString(value);
            _writer.WriteEndAttribute();
        }

        public void WriteOptionalElementString(string elementName, string value)
        {
            if (value == null)
                return;
            WriteElementString(elementName, value);
        }

        public void SetOptionalBoolean(XmlQualifiedName qualifiedName, bool? value)
        {
            setOptionalBooleanWithNamespace(qualifiedName.LocalName, qualifiedName.Namespace, value);
        }

        public void SetOptionalBoolean(string attributeName, bool? value)
        {
            if (value == null)
                return;
            SetBoolean(attributeName, value.Value); //\\
        }

        public void WriteOptionalElementList<T>(ICollection<T> items, string names, bool forceElement)
            where T : IOfficeXmlSerializable
        {
            if (items == null)
            {
                if (forceElement)
                {
                    using (Write(names))
                    {
                    }
                }
                return;
            }
            if (items.Count == 0 && !forceElement)
                return;

            using (WriteContext c = Write(names))
            {
                foreach (T item in items)
                    c.WriteElement(item);
            }
        }

        public void WriteOptionalElementList<T>(ICollection<T> items, string names) where T : IOfficeXmlSerializable
        {
            if (items == null)
                return;
            if (items.Count == 0)
                return;

            using (WriteContext c = Write(names))
            {
                foreach (T item in items)
                    c.WriteElement(item);
            }
        }

        public void WriteOptionalElementDoubleAttribute(string elementName, double? value)
        {
            if (value == null)
                return;
            WriteElementDoubleAttribute(elementName, "val", value.Value);
        }

        public void WriteOptionalElementStringAttribute(string name, string value)
        {
            if (value == null)
                return;
            WriteElementStringAttribute(name, "val", value);
        }

        public void WriteOptionalElementIntegerAttribute(string elementName, int? value)
        {
            if (value == null)
                return;
            WriteElementIntegerAttribute(elementName, "val", value.Value);
        }

        public void WriteOptionalElementsWhile(IEnumerable<IOfficeXmlSerializable> elements)
        {
            if (elements == null)
                return;
            foreach (IOfficeXmlSerializable element in elements)
            {
                WriteElement(element);
            }
        }

        public void WriteElementIntegerAttribute(string elementName, int value)
        {
            WriteElementIntegerAttribute(elementName, "val", value);
        }

        [CLSCompliant(false)]
        public void WriteElementUIntegerAttribute(string elementName, uint value)
        {
            WriteElementUIntegerAttribute(elementName, "val", value);
        }


        public void WriteOptionalElementEnumAttribute<T>(string elementName, T value, T defaultValue1, T defaultValue2)
            where T : struct
        {
            WriteOptionalElementEnumAttribute(elementName, "val", value, defaultValue1, defaultValue2);
        }

        internal void WriteElementString(string name, string prefix, string namespaceUri, string value)
        {
            using (Write(name, prefix, namespaceUri))
            {
                _writer.WriteString(value);
            }
        }

        public void WriteOptionalElementEnumAttribute<T>(string name, T value, T defaultValue) where T : struct
        {
            WriteOptionalElementEnumAttribute(name, value, defaultValue, defaultValue);
        }

        public void CopyDataFrom(WriteContext context)
        {
            _values = context._values;
        }

        public void SetUIntegerAsInteger(string p, int Id)
        {
            SetUInteger(p, (uint) Id);
        }

        public void SetOptionalUIntegerAsInteger(string attributeName, int value, int defaultValue)
        {
            SetOptionalUInteger(attributeName, (uint) value, (uint) defaultValue);
        }

        public void SetOptionalUIntegerAsInteger(string attributeName, int? value)
        {
            if (value == null)
                return;
            SetUInteger(attributeName, (uint) value.Value);
        }

        public T GetData<T>()
        {
            return (T) GetData(typeof (T));
        }

        public Uri GetAvailableUri(string format)
        {
            return GetAvailableUri(Part, format);
        }

        private static Uri GetAvailableUri(PackagePart part, string format)
        {
            for (int i = 1;; i++)
            {
                var result = new Uri(string.Format(format, i), UriKind.Relative);
                result = PackUriHelper.ResolvePartUri(part.Uri, result);
                if (!part.Package.PartExists(result))
                    return result;
            }
        }

        public void WriteOptionalUIntegerAsInteger(string elementName, int value, int defaultValue)
        {
            WriteOptionalUInteger(elementName, (uint) value, defaultValue);
        }

        public void WriteElementUIntegerAttributeAsInteger(string elementName, string attributeName, int value)
        {
            WriteElementUIntegerAttribute(elementName, attributeName, (uint) value);
        }

        public void SetOptionalInteger64(string attributeName, long value, long defaultValue)
        {
            if (value == defaultValue)
                return;
            SetInteger64(attributeName, value);
        }

        public void WriteElementBooleanAttribute(string elementName, string attributeName, bool value)
        {
            using (WriteContext c = Write(elementName))
            {
                c.SetBoolean(attributeName, value);
            }
        }


        public void DeclareNamespace(XmlNamespace xmlNamespace)
        {
            DeclareNamespace(xmlNamespace.Prefix, xmlNamespace.NamespaceUri);
        }


        private void SetStringWithNamespace(string attributeName, XmlNamespace ns, string value)
        {
            _writer.WriteStartAttribute(ns.Prefix, attributeName, ns.NamespaceUri);
            _writer.WriteString(value);
            _writer.WriteEndAttribute();
        }

        private void SetOptionalStringWithNamespace(string attributeName, XmlNamespace ns, string value)
        {
            if (value == null)
                return;
            SetStringWithNamespace(attributeName, ns, value);
        }

        public void WriteElementEnum<T>(string elementName, T value)
        {
            string s = _enumConverter.GetValue(value);
            WriteElementString(elementName, s);
        }

        private void SetOptionalEnumWithNamespace<T>(string attributeName, XmlNamespace xmlNamespace, T? value)
            where T : struct
        {
            if (value == null)
                return;
            SetEnumWithNamespace(attributeName, xmlNamespace, value.Value);
        }

        public string WriteInlinePart(CachedPart cachedPart)
        {
            PackagePart p;
            PackageRelationship relationship;
            if (_part.Package.PartExists(cachedPart.Uri))
            {
                relationship =
                    _part.CreateRelationship(cachedPart.Uri, TargetMode.Internal, cachedPart.RelationshipType);
            }
            else
            {
                CreatePart(cachedPart.Uri, cachedPart.RelationshipType, cachedPart.ContentType, out relationship, out p);
                using (Stream stream = p.GetStream())
                {
                    stream.Write(cachedPart.Data, 0, cachedPart.Data.Length);
                }
            }

            return relationship.Id;
        }

        public void SetDate(string attributeName, DateTime date)
        {
            SetString(attributeName, XmlConvert.ToString(date, XmlDateTimeSerializationMode.Unspecified).Replace("Z", ""));
        }

        public void WriteElements<T>(IEnumerable<T> items) where T : IOfficeXmlSerializable
        {
            foreach (T t in items)
            {
                WriteElement(t);
            }
        }


        public void SetOptionalInteger64(string name, long? value)
        {
            if (value == null)
                return;
            SetInteger64(name, value.Value);
        }


        public RelatedWriteContext CreateRelatedContext(XmlNamespace xmlNamespace, Uri uri, string relationshipType,
                                                        string contentType, bool forceNamespace)
        {
            RelatedWriteContext result = CreateRelatedContext(xmlNamespace, uri, relationshipType, contentType);
            result._qualifiedAttributes = forceNamespace;
            return result;
        }

        public Stream CreateRelatedStream
            (
            string name, string contentType, string relationshipType, out string rid)
        {
            WriteContext context = this;
            Uri blipUri = context.GetAvailableUri(name);
            PackagePart blipPart = context.Part.Package.CreatePart(blipUri, contentType);
            PackageRelationship rel =
                context.Part.CreateRelationship(blipUri, TargetMode.Internal,
                                                relationshipType);
            rid = rel.Id;
            return blipPart.GetStream();
        }

        private void setOptionalBooleanWithNamespace(string name, XmlNamespace ns, bool? value)
        {
            if (value != null)
            {
                SetStringWithNamespace(name, ns, value.Value ? "1" : "0");
            }
        }

        #region Nested type: EnumConverter

        private class EnumConverter
        {
            private readonly Dictionary<Type, Dictionary<object, string>> map =
                new Dictionary<Type, Dictionary<object, string>>();

            public string GetValue<T>(T s)
            {
                //   if (s == null)
                //     return default(T);
                if (!map.ContainsKey(typeof (T)))
                {
                    var m = new Dictionary<object, string>();
                    foreach (FieldInfo f in (typeof (T).GetFields(BindingFlags.Public | BindingFlags.Static)))
                    {
                        string name = f.Name;
                        foreach (XmlEnumAttribute a in f.GetCustomAttributes(typeof (XmlEnumAttribute), false))
                            name = a.Name;
                        m[f.GetValue(null)] = name;
                    }
                    map[typeof (T)] = m;
                }
                return map[typeof (T)][s];
            }
        }

        #endregion

    }
}